using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;

namespace Boggle.Net.Model
{
    public static class Extensions
    {
        public static T[] Shuffle<T>(this T[] t, Func<int, int> nextInSeries)
        {
            Contract.Requires<ArgumentNullException>(t != null);
            Contract.Requires<ArgumentNullException>(nextInSeries != null);

            for (var i = t.Length; i > 0; )
            {
                var j = nextInSeries(i--);

                var temp = t[i];
                t[i] = t[j];
                t[j] = temp;
            }

            return t;
        }

        public static string AsString(this IEnumerable<Tray.Location> path, string delimiter = ", ")
        {
            return String.Join(delimiter, path.Select(location => location.ToString()));
        }

        [Pure]
        public static bool Validate(this Tray tray)
        {
            Contract.Requires<ArgumentNullException>(tray != null);

            return
                tray.AllLetters.All(letter => Char.IsLetter(letter) && Char.IsUpper(letter)) &&
                tray.AllLetters.Count(letter => letter == 'Q') <= Game.MaxQsPerGame;
        }

        public static string LettersAt(this Tray tray, Tray.Location location)
        {
            var letter = tray.LetterAt(location);
            return letter == 'Q' ? "QU" : letter.ToString(CultureInfo.InvariantCulture);
        }

        public static IEnumerable<SearchResult> FindAllWordsIn(this Tray tray, IWordList wordList)
        {
            Contract.Requires<ArgumentException>(tray.Validate());
            Contract.Requires<ArgumentNullException>(wordList != null);

            return tray.AllLocations.AsParallel().SelectMany(location => new WordFinder(tray, wordList).WordsFoundStartingAt(location));
        }

        public static string ToRows(this Tray tray)
        {
            Contract.Requires<ArgumentException>(tray.Validate());

            var rows = tray.AllLocations
                .GroupBy(location => location.Row)
                .Select(rowLocations => String.Concat(rowLocations.Select(tray.LetterAt)));

            return String.Join(Environment.NewLine, rows);
        }

        public static int Score(this SearchResult result)
        {
            Contract.Requires<ArgumentNullException>(result != null);
            return Game.ScoreLetterCount(result.Word.Length);
        }
    }
}